﻿#region

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using JsMapper.BO;

#endregion

namespace JsMapper.Mappers
{
    public class KnockoutJs : IJsModelMapper
    {
        public string GetJs(IJsModel model)
        {
            var props = GetProperties(model);
            var propsOutput = string.Join(",", props);

            return string.Format("ko.validatedObservable({{{0}}})", propsOutput);
        }

        private IEnumerable<string> GetProperties(IJsModel model)
        {
            foreach (var p in model.Properties)
            {
                var vals = GetValidations(p, model);
                var valOut = string.Join(",", vals);
                var pOut = string.Format("{0}: ko.observable()", p.Name);

                yield return vals.Count > 0 ? string.Format("{0}.extend({{{1}}})", pOut, valOut) : pOut;
            }
        }

        private List<string> GetValidations(PropertyInfo p, IJsModel model)
        {
            var allVals = model.Validations.Where(x => x.Key == p).Select(x => x.Value).ToList();
            return allVals.Count == 0
                       ? new List<string>()
                       : allVals.SelectMany(vals => vals, (vals, v) => GetValidator(v)).ToList();
        }

        private string GetValidator(ValidationAttribute v)
        {
            if (v is RequiredAttribute) return "required: true";

            var minAttr = v as MinLengthAttribute;
            if (minAttr != null) return string.Format("minLength: {0}", minAttr.Length);

            var maxAttr = v as MaxLengthAttribute;
            if (maxAttr != null) return string.Format("maxLength: {0}", maxAttr.Length);

            var rangeAttr = v as RangeAttribute;
            if (rangeAttr != null)
                return string.Format("minLength: {0}, maxLength: {1}", rangeAttr.Minimum, rangeAttr.Maximum);

            if (v is EmailAddressAttribute) return "email: true";

            return string.Empty;
        }
    }
}